package com.coffee.log.capture;


import cn.hutool.core.util.StrUtil;
import com.coffee.concurrent.CoffeeExecutorManager;
import com.coffee.core.CoffeeHeader;
import com.coffee.core.JSONUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.stream.Collectors;

@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class OperationLogAspect {

    private final CoffeeSpELParser coffeeSpELParser;
    private final CoffeeOperationLogHandler coffeeOperationLogHandler;
    private final HttpServletRequest request;

    @Pointcut("@annotation(operationLog)")
    public void pointcut(OperationLog operationLog) {
    }

    @Around(value = "pointcut(operationLog) && @annotation(com.coffee.log.capture.OperationLog)",
            argNames = "point, operationLog")
    public Object logAround(ProceedingJoinPoint point, OperationLog operationLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        String status = "SUCCESS";
        String errorMsg = "";

        try {
            result = point.proceed();
            return result;
        } catch (Exception e) {
            status = "FAIL";
            errorMsg = e.getMessage();
            throw e;
        } finally {

            Long userId = CoffeeHeader.getUserId(request);
            String userCode = CoffeeHeader.getUserCode(request);
            OperationLogBO logEntity = buildLogEntity(
                    point, operationLog,
                    result, status, errorMsg,
                    System.currentTimeMillis() - startTime,
                    userId,
                    userCode
            );

            CoffeeExecutorManager.getInstance().getDisruptorByUserId(userId).execute(() ->  coffeeOperationLogHandler.handler(logEntity) );

        }
    }

    private OperationLogBO buildLogEntity(ProceedingJoinPoint joinPoint,
                                          OperationLog annotation,
                                          Object result,
                                          String status,
                                          String errorMsg,
                                          long costTime,
                                          Long userId,
                                          String userCode) {
        // 解析方法参数
        String params = annotation.logParams() ? Arrays.stream(joinPoint.getArgs())
                .map(JSONUtils::toJson)
                .collect(Collectors.joining(",")) : null;

        // 解析SpEL描述
        String dynamicDesc = StrUtil.isNotEmpty(annotation.descSpEl()) ? coffeeSpELParser.parse(
                annotation.descSpEl(),
                ((MethodSignature) joinPoint.getSignature()).getMethod(),
                joinPoint.getArgs(),
                result,
                errorMsg
        ) : null;

        return OperationLogBO.builder()
                .app(annotation.scope())
                .name(annotation.name())
                .description(dynamicDesc)
                .params(params)
                .result(annotation.logResult() ? JSONUtils.toJson(result) : "")
                .status(status)
                .errorMsg(errorMsg)
                .costTime(costTime)
                .userId(userId)
                .userCode(userCode)
                .build();
    }

}
